﻿using Moven.ViewModels;
using Xamarin.Forms;
using System.Linq;
using FormsToolkit;
using System.Collections.Generic;
using System.Threading.Tasks;
using Moven.Controls;
using Moven.Data;
using System.ComponentModel;
using System;
using Moven.Helpers;

#if __ANDROID__
using Android.Support.Design.Widget;
using Android.Widget;
using Moven.Droid;
#elif __IOS__
using MaterialControls;
using Moven.iOS;
#endif

namespace Moven.Views
{
	public partial class BookmarksPage
	{
		class DeletedArticle : DeletedItem<ArticleWithBookmark> { }
		class ArticlesDeletion : List<DeletedArticle> { } 

		ExtendedToolbarItem _clearButton, _undoButton, _deleteButton, _selectAllButton, _unselectAllButton, _syncButton;

	    readonly List<ArticlesDeletion> _deletions = new List<ArticlesDeletion>();

		public BookmarksPage()
		{
			InitializeComponent();
			BindingContext = new ArticlesViewModel("收藏", MenuType.Bookmarks, Statics.GetBookmarkedArticles())
			{
				Navigation = Navigation,
				ArticlesList = listView
			};
			Articles.ForEach(AddPropertyChangedHandler);

			if (IsIOS)
			{
				CreateToolbarButtons();
			}

			MessagingService.Current.Subscribe<ArticleWithBookmark>(
				Statics.ArticleChangedMessageId, (_, article) =>
				{
					if (_deletions.Any())
					{
						var articleIndex = article.Index;
						var allDeletedArticles = _deletions.SelectMany(x => x).ToList();
						var indexOffset = article.IsBookmarked ? 1 : -1;
						allDeletedArticles.ForEach(x => x.OriginalIndex += indexOffset);
						Func<DeletedArticle, bool> finder = x => x.Item.Index == articleIndex;
						if (article.IsBookmarked && allDeletedArticles.Any(finder))
						{
							// Deleted bookmark got re-added, since new bookmark will be at the top,
							// this will mess up the indices a little bit, 
							// so we need to first remove the article from the deletion, 
							// then we remove potential empty deletion
							var invalidatedDeletion = _deletions.First(deletion => deletion.Any(finder));
							invalidatedDeletion.RemoveAt(invalidatedDeletion.FindIndex(x => finder(x)));
							if (!invalidatedDeletion.Any())	// Empty deletion
							{
								_deletions.Remove(invalidatedDeletion);
							}
						}
					}
					
					var article2 = Articles.FirstOrDefault(x => x.Index == article.Index);
					if (article.IsBookmarked)
					{
						if (article2 == null)
						{
							article.IsSelected = false;
							Articles.Insert(0, article);
							AddPropertyChangedHandler(article);
						}
					}
					else
					{
						if (article2 != null)
						{
							Articles.Remove(article2);
							RemovePropertyChangedHandler(article2);
						}
					}
				}
			);

			Statics.ArticlesStore.BookmarksSynced += (sender, e) =>
			{
				if (e.Status == SyncBookmarksStatus.SuccessWithChanges)
				{
					Articles.ForEach(RemovePropertyChangedHandler);
					Articles.Clear();
					var bookmarkedArticles = Statics.GetBookmarkedArticles(e.SyncedBookmarks.ToList());

					bookmarkedArticles.ForEach(article =>
					{
						AddPropertyChangedHandler(article);
						Articles.Add(article);
					});
					_deletions.Clear();
				}
			};
		}

		async Task DoDeleteAnimation(IEnumerable<ArticleWithBookmark> articles)
		{
			var animationTasks = articles.Where(x => x.ViewCell != null).
						 Select(async article =>
							{
								await article.View.ScaleTo(0.6, 200, Easing.Linear);
								return article.View.RotateTo(90, 300, Easing.Linear);
							}).ToArray();
			await Task.WhenAll(animationTasks);
			await Task.Delay(TimeSpan.FromMilliseconds(50));
		}

      	async Task DeleteOrClearArticles(bool isClear = false)
		{
			DismissSnackBars();

			var deletion = new ArticlesDeletion();
			var articlesToDelete = isClear ? Articles.ToList() : Articles.Where(x => x.IsSelected).ToList();
			articlesToDelete.Select((x, index) => new DeletedArticle { Item = x, OriginalIndex = index }).
                            ForEach(deletion.Add);
			var deletionCount = deletion.Count;
			isClear = deletionCount == Articles.Count;

			_deletions.Add(deletion);

			SaveBookmarkOperation(deletion);

			await DoDeleteAnimation(articlesToDelete);

			ShowSnackBar($"{deletionCount}个收藏已删除", "撤销", async () => await UndoDelete());

			articlesToDelete.ForEach(RemovePropertyChangedHandler);
			if (isClear)
			{
				Articles.Clear();
			}
			else
			{
				articlesToDelete.ForEach(article => Articles.Remove(article));
			}
			await SaveBookmarks();
		}

		async Task SaveBookmarkOperation(ArticlesDeletion deletion, BookmarkAction action = BookmarkAction.Remove)
		{
			var operation = new BookmarkOperation
			{
				Action = action,
				Indices = deletion.Select(x => x.Item.Index).ToArray()
			};
			await Statics.ArticlesStore.AddBookmarkOperationAsync(operation);
		}

		protected async override void OnAppearing()
		{
			if (IsAndroid)
			{
				ToolbarItems.Clear();
				CreateToolbarButtons();
			}

			base.OnAppearing();

			ShowLongPressToggleCheckTip();

#if __ANDROID__
			await Task.Delay(TimeSpan.FromMilliseconds(200));
			AndroidListView.ItemLongClick += OnArticleLongClick;
#endif
		}

		protected override void OnDisappearing()
		{
			DismissSnackBars();
#if __ANDROID__
			AndroidListView.ItemLongClick -= OnArticleLongClick;
			_androidListView = null;
#endif
			base.OnDisappearing();
		}

		protected override void UpdateUI()
		{
			base.UpdateUI();
			UpdateEmptyMessage();
			UpdateClearButton();
			UpdateDeleteButton();
			UpdateUndoButton();
			UpdateSelectAllButton();
			UpdateUnselectAllButton();
		}

		async Task SaveBookmarks()
		{
			UpdateUI();
			var newBookmarks = Articles.Select(x => x.Index).ToList();
			await Statics.ArticlesStore.SaveBookmarksAsync(newBookmarks);
		}

		void CreateToolbarButtons()
		{
			_deleteButton = new ExtendedToolbarItem(ToolbarItems, "delete.png",
												   IsAndroid ? (FileImageSource)"delete_disabled.png" : null)
			{
				Text = "删除",
				Command = new Command(async () => await DeleteOrClearArticles(), () => Articles.Any(x => x.IsSelected))
			};

			_clearButton = new ExtendedToolbarItem(ToolbarItems, "clear.png",
												   IsAndroid ? (FileImageSource)"clear_disabled.png" : null)
			{
				Text = "清空",
				Command = new Command(async () => await DeleteOrClearArticles(true), () => Articles.Any())
			};

			_undoButton = new ExtendedToolbarItem(ToolbarItems,
												  "undo.png",
												   IsAndroid ? (FileImageSource)"undo_disabled.png" : null)
			{
				Text = "撤销删除",
				Command = new Command(async () => await UndoDelete(), () => _deletions.Any())
			};

			_selectAllButton = new ExtendedToolbarItem(ToolbarItems)
			{
				Text = "全选",
				Order = ToolbarItemOrder.Secondary,
				Command = new Command(() => Articles.ForEach(x => x.IsSelected = true), () => Articles.Any() && !Articles.All(x => x.IsSelected))
			};

			_unselectAllButton = new ExtendedToolbarItem(ToolbarItems)
			{
				Text = "全不选",
				Order = ToolbarItemOrder.Secondary,
				Command = new Command(() => Articles.ForEach(x => x.IsSelected = false), () => Articles.Any(x => x.IsSelected))
			};

			_syncButton = new ExtendedToolbarItem(ToolbarItems)
			{
				Text = "同步",
				Icon = "sync.png",
				Order = ToolbarItemOrder.Secondary,
				Command = new Command(async () => await SyncBookmarks())
			};
		}

		async Task UndoDelete()
		{
			DismissSnackBars();
			var lastDeletion = _deletions.Last();
			_deletions.RemoveAt(_deletions.Count - 1);

			foreach (var deletionItem in lastDeletion)
			{
				var article = deletionItem.Item;
				Articles.Insert(deletionItem.OriginalIndex, article);
				article.IsSelected = false;
				AddPropertyChangedHandler(article);
			}

			SaveBookmarkOperation(lastDeletion, BookmarkAction.Add);

			ShowSnackBar($"{lastDeletion.Count}个收藏已恢复", "重删", () =>
			{
				lastDeletion.Select(x => x.Item).ForEach(article => Articles.Remove(article));
				_deletions.Add(lastDeletion);
				SaveBookmarkOperation(lastDeletion);
				UpdateUI();
			});

			await SaveBookmarks();
		}

#if __ANDROID__

	    readonly List<Snackbar> _snackBars = new List<Snackbar>();

		Android.Widget.ListView _androidListView;
		public Android.Widget.ListView AndroidListView => _androidListView ?? 
		              (_androidListView = RootView.FindView<Android.Widget.ListView>());

		Snackbar ShowSnackBar(string message, string actionText, Action action, int duration = 4000)
		{
			var snackBar = Snackbar.Make(AndroidListView, message, duration).SetAction(
				actionText, view => action());
			snackBar.Show();
			_snackBars.Add(snackBar);
			return snackBar;
		}

		void OnArticleLongClick(object sender, AdapterView.ItemLongClickEventArgs e)
		{
			var index = e.Position - 1;
			var article = Articles[index];
			article.ToggleCommand.Execute(null);
		}

#elif __IOS__

	    readonly List<MDSnackbar> _snackBars = new List<MDSnackbar>();

		MDSnackbar ShowSnackBar(string message, string actionText, Action action, int duration = 4000)
		{
			var snackBar = new MDSnackbar(message, actionText, duration / 1000.0);
			snackBar.AddActionTouchedHandler(action);
			snackBar.Show();
			_snackBars.Add(snackBar);
			return snackBar;
		}

#endif

		void DismissSnackBars()
		{
			_snackBars.ForEach(snackBar => { snackBar.Dismiss(); snackBar.Dispose(); });
			_snackBars.Clear();
		}
		
		void AddPropertyChangedHandler(ArticleWithBookmark article) => article.PropertyChanged += OnIsSelectedChanged;

		void RemovePropertyChangedHandler(ArticleWithBookmark article) => article.PropertyChanged -= OnIsSelectedChanged;

		void OnIsSelectedChanged(object sender, PropertyChangedEventArgs args)
		{
			var article = new ArticleWithBookmark(new Article());
			if (args.PropertyName == nameof(article.IsSelected))
			{
				UpdateDeleteButton();
				UpdateSelectAllButton();
				UpdateUnselectAllButton();
				ViewModel.UpdateTitle();
			}
		}

		void UpdateUndoButton() => _undoButton.UpdateEnableDisable();

		void UpdateClearButton() => _clearButton.UpdateEnableDisable();

		void UpdateDeleteButton() => _deleteButton.UpdateEnableDisable();

		void UpdateSelectAllButton() => _selectAllButton.UpdateEnableDisable();

		void UpdateUnselectAllButton() => _unselectAllButton.UpdateEnableDisable();

		void UpdateEmptyMessage() => emptyMessage.IsVisible = !Articles.Any();

		async Task SyncBookmarks()
		{
			await Navigation.PushModalAsync(new SyncPage());
		}

		bool _isLongPressToggleCheckTipShown;
		void ShowLongPressToggleCheckTip()
		{
			if (!_isLongPressToggleCheckTipShown && Settings.ShowLongPressToggleCheckTip)
			{
				ShowSnackBar("长按收藏墨文可以切换勾选状态", "不再显示", () =>
				{
					Settings.ShowLongPressToggleCheckTip = false;
				});
			}
			_isLongPressToggleCheckTipShown = true;
		}
	}
}
